home *** CD-ROM | disk | FTP | other *** search
- /*
- * Blob Manager Demonstration: Arithmetic problem module
- *
- * Displays arithmetic problems to solve. Allows radix to be set
- * anywhere from base 2 to base 10.
- *
- * This module demonstrates a simple use of the BlobClick advisory
- * function.
- *
- * 26 July 1986 Paul DuBois
- */
-
- # include "TransSkel.h"
-
- # include "BlobMgr.h"
- # include "BlobDemo.h"
-
-
- /*
- * Blob types, used to distinguish different parts of problem
- */
-
- enum
- {
- donorBlob, /* for donor digits */
- carryBlob, /* for carry digits */
- addendBlob, /* for addend digits */
- sumBlob, /* for sum digits */
- operBlob /* for the plus sign */
- };
-
-
- # define carryPos 10 /* horizontal position of carry digits */
- /* addend and sum pos's are relative to this */
- # define digitPos 110 /* position of donor digits */
- # define digitSize 18 /* size of digit blobs */
- # define digitGap 2 /* gap between blobs */
- # define absMaxLen 10 /* absolute max number of digits in addends */
-
-
- static WindowPtr wind;
- static MenuHandle radixMenu;
- static ControlHandle checkAns;
- static ControlHandle giveUp;
- static ControlHandle nextProb;
-
- static short radix = 10; /* initially decimal */
-
- static BlobSetHandle digits = nil; /* donor blobs */
- static BlobSetHandle problem = nil; /* receptor blobs */
- static BlobHandle firstAddendBlob;
- static BlobHandle firstSumBlob;
- static BlobHandle plusSignBlob;
- static short maxLen = 6; /* current max digits in addends */
- static short rightEdge; /* right edge of problem display */
- static short lineX; /* pos and length of line */
- static short lineY; /* between lower addend and sum */
- static short lineLen;
-
- static Boolean paused;
-
-
- /*
- * Pick a problem digit. Don't pick zero if canBeZero is false.
- */
-
- static short
- PickDigit (Boolean canBeZero)
- {
- short n;
-
- do {
- n = BlobRand (radix-1); /* use Blob Manager rand function */
- } while (!canBeZero && n == 0);
- return (n);
- }
-
-
- /*
- * Make a blob. Pass the x and y coordinates, the character drawn in the
- * blob, whether it needs an explicit match or not. Carry digits don't
- * need an explicit match, digits of the sum do - except that the leftmost
- * sum blob might be unnecessary - so it can be zero or nothing.
- */
-
- static BlobHandle
- MakeBlob (bSet, x, y, c, needMatch, type)
- BlobSetHandle bSet;
- short x, y;
- short c;
- Boolean needMatch;
- short type;
- {
- BlobHandle b;
-
- b = MakeCharBlob (bSet, false, infiniteGlue, needMatch,
- x, y, c);
- SetBRefCon (b, c + ((long) type << 16));
- return (b);
- }
-
-
- /*
- * Return blob type, encoded as the high word of the reference
- * constant.
- */
-
- static short
- GetBlobType (BlobHandle b)
- {
- return (HiWord (GetBRefCon (b)));
- }
-
-
- /*
- * Generate a new problem to solve. The addends are at least two digits
- * long. The max number of digits in the sum will be 1 greater than the
- * longest addend. The max number of carry digits is the same as the length
- * of the longest addend.
- *
- * Digits in the addend and sum arrays are numbered from right to left.
- */
-
- static void
- GenerateProblem (void)
- {
- short addend1[absMaxLen];
- short addend2[absMaxLen];
- short carry[absMaxLen];
- short sum[absMaxLen+1];
- short add1Len; /* number of digits in addend 1 */
- short add2Len; /* number of digits in addend 2 */
- short sumLen; /* number of digits in sum */
- short carryLen; /* number of carry digits */
- BlobHandle b;
- short i;
- short x, y;
-
- add1Len = BlobRand (maxLen - 2) + 2; /* addends are at least 2 digits */
- add2Len = BlobRand (maxLen - 2) + 2;
- carryLen = (add1Len > add2Len ? add1Len : add2Len);
- sumLen = carryLen + 1;
-
- /* zero the addends and the carry digits */
-
- for (i = 0; i < absMaxLen; ++i)
- {
- addend1[i] = 0;
- addend2[i] = 0;
- carry[i] = 0;
- }
-
- /* generate digits for the addends and determine the sum. */
- /* the leftmost digit of the addends should not be zero */
-
- for (i = 0; i < add1Len; ++i)
- addend1[i] = PickDigit (i != add1Len - 1);
- for (i = 0; i < add2Len; ++i)
- addend2[i] = PickDigit (i != add2Len - 1);
- for (i = 0; i < sumLen; ++i)
- {
- sum[i] = addend1[i] + addend2[i];
- if (i > 0 && sum[i-1] >= radix)
- {
- sum[i-1] -= radix;
- ++sum[i];
- ++carry[i-1];
- }
- }
-
- /* get rid of any old problem blob set */
-
- if (problem != nil)
- {
- HideBlobSet (problem);
- DisposeBlobSet (problem);
- }
- problem = NewBlobSet ();
-
- /*
- * Generate blobs for carry digits - they don't need an explicit
- * match. Carry digits are placed over every addend digit
- * except the rightmost
- */
-
- x = rightEdge - digitSize;
- y = carryPos;
- for (i = 0; i < carryLen; ++i)
- {
- x -= digitSize + digitGap;
- b = MakeBlob (problem, x, y, ' ', false, carryBlob);
- NewBlobMatch (GetBlobHandle (digits, carry[i]), b);
- }
-
- /*
- * Generate blobs for addends. addend blobs are given a non-zero
- * reference value so the advisory function can distinguish them
- * from carry and sum blobs easily.
- */
-
- x = rightEdge - digitSize;
- y += digitSize + digitGap;
- for (i = 0; i < add1Len; ++i)
- {
- b = MakeBlob (problem, x, y, ' ', false, addendBlob);
- GlueGlob (GetBlobHandle (digits, addend1[i]), b);
- if (i == 0)
- firstAddendBlob = b;
- x -= digitSize + digitGap;
- }
-
- x = rightEdge - digitSize;
- y += digitSize + digitGap;
- for (i = 0; i < add2Len; ++i)
- {
- b = MakeBlob (problem, x, y, ' ', false, addendBlob);
- GlueGlob (GetBlobHandle (digits, addend2[i]), b);
- x -= digitSize + digitGap;
- }
-
- /* add the plus sign */
-
- plusSignBlob = MakeBlob (problem, x, y, '+', false, operBlob);
- FreezeBlob (plusSignBlob);
-
- /* figure out length and position of line 'tween addend2 and sum */
-
- y += digitSize + digitGap;
- lineLen = sumLen * (digitSize + digitGap) - digitGap;
- lineX = rightEdge - lineLen;
- lineY = y;
-
- /*
- * Make sum digits. These must be matched explicitly, except
- * possibly the leftmost one.
- */
-
- x = rightEdge - digitSize;
- y += digitGap + 2;
- for (i = 0; i < sumLen; ++i)
- {
- b = MakeBlob (problem, x, y, ' ', true, sumBlob);
- if (i == sumLen - 1 && sum[i] == 0)
- ClearBlobFlags (b, bNeedGlobMask); /* explicit match unneeded */
- NewBlobMatch (GetBlobHandle (digits, sum[i]), b);
- if (i == 0)
- firstSumBlob = b;
- x -= digitSize + digitGap;
- }
- }
-
-
- static void
- DrawLine (void)
- {
- MoveTo (lineX, lineY);
- LineTo (rightEdge, lineY);
- }
-
-
- static void
- NextProblem (void)
- {
- InvalRect (&wind->portRect);
- PenMode (patBic);
- DrawLine (); /* erase line */
- PenNormal ();
- SetCTitle (checkAns, "\pCheck");
- HiliteControl (checkAns, normalHilite); /* make sure these buttons are on */
- HiliteControl (giveUp, normalHilite);
- paused = false;
- /* choose numbers */
- GenerateProblem ();
- ShowBlobSet (problem);
- DrawLine ();
- ValidRect (&wind->portRect);
- }
-
-
- /*
- * Make digit set - creates only the digits that are legal for the
- * current radix.
- */
-
- static void
- MakeDigits (void)
- {
- short i, x;
-
- if (digits != nil)
- {
- HideBlobSet (digits);
- DisposeBlobSet (digits);
- }
- digits = NewBlobSet ();
- x = rightEdge - radix * (digitSize + digitGap) + digitGap;
- for (i = 0; i < radix; ++i)
- {
- (void) MakeBlob (digits, x, digitPos, i + '0', false, donorBlob);
- x += digitSize + digitGap;
- }
- ShowBlobSet (digits);
- }
-
-
- /*
- * Radix menu handler
- *
- * Change the radix and choose a new problem.
- */
-
- static pascal void
- ChooseRadix (short item)
- {
- CheckItem (radixMenu, radix-1, false);
- radix = item + 1; /* menu items start with Base 2 */
- CheckItem (radixMenu, radix-1, true);
- MakeDigits ();
- NextProblem ();
- }
-
-
- static pascal void
- Mouse (Point pt, long t, short mods)
- {
- BlobHandle b;
- short type;
- ControlHandle ctl;
-
- if (FindControl (pt, wind, &ctl))
- {
- if (TrackControl (ctl, pt, nil)) /* any button hit? */
- {
- if (ctl == nextProb)
- NextProblem ();
- else if (ctl == checkAns)
- {
- if (!paused)
- {
- /* give feedback (this freezes the receptors) */
- BlobFeedback (problem, normalDraw, dimDraw);
- SetCTitle (checkAns, "\pResume");
- paused = true;
- }
- else
- {
- /* allow user to continue working */
- SetCTitle (checkAns, "\pCheck");
- paused = false;
- ThawBlobSet (problem); /* thaw entire problem */
- FreezeBlob (plusSignBlob); /* except plus sign */
- }
- }
- else if (ctl == giveUp)
- {
- /*
- * Show answer. Show only the non-zero carry digits,
- * and the sum digits. Show the leftmost sum digit only
- * if it's non-zero. Have to that the problem first,
- * because some of it may have been dimmed by Check.
- */
- ThawBlobSet (problem); /* might be frozen from Check */
- for (b = FirstBlob (problem); NextBlob (b) != nil; b = NextBlob (b))
- {
- type = GetBlobType (b);
- if (type == carryBlob)
- {
- UnglueGlob (b); /* clear any glob it might have */
- if (FirstBMatch (b) == GetBlobHandle (digits, 1))
- ZGlueGlob (FirstBMatch (b), b);
- }
- else if (type == sumBlob)
- ZGlueGlob (FirstBMatch (b), b);
- }
- /*
- * for loop leaves b pointing at last blob in problem set,
- * i.e., the leftmost sum digit.
- */
- if (FirstBMatch (b) != GetBlobHandle (digits, 0))
- ZGlueGlob (FirstBMatch (b), b);
-
- HiliteControl (checkAns, dimHilite); /* make inactive */
- HiliteControl (giveUp, dimHilite);
- paused = true;
- }
- }
- }
- else if (!paused)
- {
- BlobClick (pt, t, digits, problem);
- if (BlobSetQuiet (problem)) /* done yet? */
- {
- HiliteControl (checkAns, dimHilite);
- HiliteControl (giveUp, dimHilite);
- paused = true;
- }
- }
- }
-
-
- /*
- * The purpose of the advisory is to allow digits from the addends
- * to be duplicated onto carry or sum digits, but to prevent addend
- * blobs from being cleared or duplicated onto, and to prevent donors
- * from being glued to them. So, whenever a clear, glue, or duplicate
- * message if received, return false if the blob involved is an addend
- * to make BlobClick abort. Addends are easily distinguished since
- * they have non-zero reference values.
- *
- * Transfer and swap messages will never be received since the
- * permissions are set to disallow those transaction types.
- * For any other message, return true to continue normal processing.
- */
-
- static pascal Boolean
- Advisory (short mesg, BlobHandle b)
- {
- switch (mesg)
- {
- case advGlue:
- case advUnglue:
- case advDup:
- if (GetBlobType (b) == addendBlob)
- return (false);
- }
- return (true);
- }
-
-
- static pascal void
- Update (Boolean resized)
- {
- GrafPtr port;
-
- GetPort (&port);
- EraseRect (&port->portRect);
-
- DrawControls (wind);
- DrawBlobSet (problem);
- DrawLine ();
- DrawBlobSet (digits);
- }
-
-
- static pascal void
- Activate (Boolean active)
- {
- if (active)
- {
- SetDragRects (wind);
- SetBCPermissions (true, true, true, false, true);
- SetBCAdvisory (Advisory);
- radixMenu = GetMenu (radixMenuRes);
- SkelMenu (radixMenu, ChooseRadix, DoMClobber, false, true);
- CheckItem (radixMenu, radix-1, true);
- }
- else
- {
- SkelRmveMenu (radixMenu); /* destroy handler */
- SetBCAdvisory (nil);
- }
- }
-
-
- void
- RadixInit (void)
- {
- Rect r;
-
- SkelWindow (wind = GetDemoWind (radixWindRes),
- Mouse, /* mouse clicks */
- nil, /* key clicks */
- Update, /* updates */
- Activate, /* activate/deactivate events */
- nil, /* close window */
- DoWClobber, /* dispose of window */
- nil, /* idle proc */
- false); /* irrelevant, since no idle proc */
-
- SetRect (&r, 0, 25, 70, 45);
- OffsetRect (&r, wind->portRect.right - 80, 0);
- checkAns = NewControl (wind, &r, "\pCheck", true, 0, 0, 0,
- pushButProc, 0L);
- OffsetRect (&r, 0, 30);
- giveUp = NewControl (wind, &r, "\pUncle", true, 0, 0, 0,
- pushButProc, 0L);
- OffsetRect (&r, 0, 30);
- nextProb = NewControl (wind, &r, "\pNext", true, 0, 0, 0,
- pushButProc, 0L);
-
- rightEdge = wind->portRect.right - 100;
- SetCharBlobSize (digitSize);
- MakeDigits ();
- NextProblem ();
-
- MakeFrontWind (wind);
- }
-